package cn.erbudev.model;

import cn.erbudev.util.DrawUtil;

import java.util.ArrayList;
import java.util.List;

public class Arc extends DrawEntity {
    private Point centre;
    private double radius;
    private double startAngle;
    private double endAngle;
    private double angleDelta;
    private List<double[]> angleRanges = new ArrayList<>();


    //优弧构造函数
    public Arc(Point centre, Point startPoint, Point endPoint, double angleDelta) {
        this.centre = centre;
        this.startPoint = startPoint;
        this.endPoint = endPoint;
        this.radius = (centre.getDistance(startPoint) + centre.getDistance(endPoint)) / 2.0;
        this.startAngle = getAngle(centre, startPoint);
        this.endAngle = getAngle(centre, endPoint);
        this.angleDelta = angleDelta;
        if (angleDelta > 0) {
            if (this.startAngle > this.endAngle) {
                this.angleRanges.add(new double[]{0, this.endAngle});
                this.angleRanges.add(new double[]{this.startAngle, 2 * Math.PI});
            } else {
                this.angleRanges.add(new double[]{this.startAngle, this.endAngle});
            }
        } else {
            if (this.startAngle > this.endAngle) {
                this.angleRanges.add(new double[]{this.endAngle, this.startAngle});
            } else {
                this.angleRanges.add(new double[]{0, this.startAngle});
                this.angleRanges.add(new double[]{this.endAngle, 2 * Math.PI});
            }
        }
    }

    //劣弧构造函数
    public Arc(Point centre, Point startPoint, Point endPoint) {
        this.centre = centre;
        this.startPoint = startPoint;
        this.endPoint = endPoint;
        this.radius = (centre.getDistance(startPoint) + centre.getDistance(endPoint)) / 2.0;
        this.startAngle = getAngle(centre, startPoint);
        this.endAngle = getAngle(centre, endPoint);
        this.angleRanges = new ArrayList<>();
        if (this.startAngle > this.endAngle) {
            this.angleDelta = (this.endAngle - 0) + (2 * Math.PI - this.startAngle);
            this.angleRanges.add(new double[]{0, this.endAngle});
            this.angleRanges.add(new double[]{this.startAngle, 2 * Math.PI});
        } else {
            this.angleDelta = this.endAngle - this.startAngle;
            this.angleRanges.add(new double[]{this.startAngle, this.endAngle});
        }
    }


    public boolean isInferiorArc() {
        return Math.abs(angleDelta) < Math.PI;
    }


    public Point getCentre() {
        return centre;
    }

    public void setCentre(Point centre) {
        this.centre = centre;
    }

    public Point getStartPoint() {
        return startPoint;
    }

    public void setStartPoint(Point startPoint) {
        this.startPoint = startPoint;
    }

    public Point getEndPoint() {
        return endPoint;
    }

    public void setEndPoint(Point endPoint) {
        this.endPoint = endPoint;
    }

    public double getRadius() {
        return radius;
    }

    public void setRadius(double radius) {
        this.radius = radius;
    }

    public double getStartAngle() {
        return startAngle;
    }

    public void setStartAngle(double startAngle) {
        this.startAngle = startAngle;
    }

    public double getEndAngle() {
        return endAngle;
    }

    public void setEndAngle(double endAngle) {
        this.endAngle = endAngle;
    }

    public List<double[]> getAngleRanges() {
        return angleRanges;

    }

    @Override
    public List<Point> getIntersection(DrawEntity other) {
        List<Point> points = new ArrayList<Point>();
        switch (other.getType()) {
            case DrawEntity.LINE:
                return other.getIntersection(this);
            case DrawEntity.ARC:
                return DrawUtil.getArc2ArcCrossPoint(this, (Arc) other);
            default:
                break;
        }
        return points;
    }


    public boolean isInnerAngle(double angle) {
        boolean result = false;
        for (double[] range : angleRanges) {
            result = result || angle >= range[0] && angle <= range[1];
        }
        return result;
    }

    public boolean isInnerPoint(Point point) {
        double angle = this.getAngle(this.centre, point);
        return isInnerAngle(angle) && Math.abs(point.getDistance(this.centre) - this.radius) <= DrawUtil.Error;
    }

    @Override
    public List<Point> getChildCrossPoints(DrawEntity other, double d) {
        switch (other.getType()) {
            case DrawEntity.LINE:
                return DrawUtil.getLine2ArcIntersection((Line) other, this, d);
            case DrawEntity.ARC:
                return DrawUtil.getArc2ArcIntersection(this, (Arc) other, d);
            default:
                return new ArrayList<>();
        }
    }

    //flag:true 改变startPoint; flag:false 改变endPoint 并返回相同ID的缩小图元
    @Override
    public DrawEntity reduce(Point point, boolean flag) {
        Arc arc = flag ? new Arc(centre, point, endPoint) : new Arc(centre, startPoint, point);
        arc.setID(arc.getID());
        return arc;
    }

    @Override
    public double getEntityLength() {
        return angleDelta*radius;
    }

    public double getAngle(Point start, Point end) {
        double x = end.getX() - start.getX();
        double y = end.getY() - start.getY();
        double angle = Math.atan(y / x);
        if (angle < 0)
            angle += Math.PI;
        if (y < -DrawUtil.Error)
            angle += Math.PI;
        else if (Math.abs(y) < DrawUtil.Error) {
            if (x > -DrawUtil.Error)
                angle = 0;
            else
                angle = Math.PI;

        }
        return angle;
    }

    public double getAngleDelta() {
        return angleDelta;
    }


    @Override
    public int getType() {
        return DrawEntity.ARC;
    }

    @Override
    public String toString() {
        return "Arc{" +
                "centre=" + centre +
                ", startPoint=" + startPoint +
                ", endPoint=" + endPoint +
                ", radius=" + radius +
                ", startAngle=" + startAngle +
                ", endAngle=" + endAngle +
                '}';
    }
}
